#include "wl_audio.h"
#include "wl_core.h"
#include "wl_malloc.h"
#include "wl_trace.h"
#include "wl_utils.h"
#include "wl_ext_os.h"
#include "wl_ext_player.h"
#include "wl_ext_recorder.h"

#define INVALID_AUDIO_HANDLE 0

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

typedef struct __wl_audio_recorder {
    wl_ext_mutex               mutex;
    wl_audio_recorder_callback callback;
    wl_audio_end_callback      end_callback;
    wl_uint8                   cache[RECORD_PCM_CACHED_LEN];
    wl_int32                   cachelen;
    wl_ext_recorder_handle     handle;
} wl_audio_recorder;

typedef struct __wl_audio_player {
    wl_ext_mutex             mutex;
    wl_audio_player_callback callback;
    wl_audio_end_callback    end_callback;
    wl_int32                 cachelen;
    wl_uint8                 cache[PLAY_PCM_CACHED_LEN];
    wl_ext_player_handle     handle;
} wl_audio_player;

typedef struct __wl_audio_global {
    wl_bool           initialized;
    wl_audio_recorder recorder;
    wl_audio_player   player;
} wl_audio_global;
static wl_audio_global s_audio_global;

wl_int32 audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size);
wl_int32 audio_get_play_data(wl_uint8* data, wl_uint32 datalen);

wl_int32 wl_audio_init(void) {
    WLLOGI("audio init");
    if (!s_audio_global.initialized) {
        if (0 != wl_ext_mutex_create(&s_audio_global.player.mutex, "player")) {
            WLLOGW("wl_mutex_create player fail");
            return -1;
        }
        if (0 != wl_ext_mutex_create(&s_audio_global.recorder.mutex, "recorder")) {
            WLLOGW("wl_mutex_create recorder fail");
            wl_ext_mutex_destroy(&s_audio_global.player.mutex);
            return -1;
        }
        
        WLLOGD("audio init wl_ext_player_init");
        if (wl_ext_player_init(16000) != 0) {
            WLLOGW("wl_ext_player_init fail");
            wl_ext_mutex_destroy(&s_audio_global.player.mutex);
            wl_ext_mutex_destroy(&s_audio_global.recorder.mutex);
            return -1;
        }

        if (wl_ext_record_init(16000) != 0) {
            WLLOGW("wl_ext_record_init fail");
            wl_ext_mutex_destroy(&s_audio_global.player.mutex);
            wl_ext_mutex_destroy(&s_audio_global.recorder.mutex);
            return -1;
        }

        s_audio_global.initialized = wl_true;
    }
    WLLOGI("audio init success");
    return 0;
}

wl_int32 wl_audio_end(void) {
    if (s_audio_global.initialized) {
        wl_ext_mutex_unlock(s_audio_global.player.mutex);
        wl_ext_mutex_unlock(s_audio_global.recorder.mutex);

        wl_ext_mutex_destroy(s_audio_global.player.mutex);
        wl_ext_mutex_destroy(s_audio_global.recorder.mutex);
        s_audio_global.initialized = wl_false;

        wl_ext_player_deinit();
        wl_ext_record_deinit();
    }
    return 0;
}

static void audio_recorder_callback(wl_ext_recorder_handle handle, int event, void* pContext) {
    switch (event) {
        case WL_EXT_RECORD_STARTED: {
            WLLOGD("audio recorder started");
        }
        break;

        case WL_EXT_RECORD_DATA: {
            int framelen = 0;
            wl_ext_mutex_lock(s_audio_global.recorder.mutex, WL_EXT_WAIT_FOREVER);
            if (handle != s_audio_global.recorder.handle) {
                if (s_audio_global.recorder.end_callback) {
                    s_audio_global.recorder.end_callback(INVALID_AUDIO_HANDLE, 0);
                }
                wl_ext_mutex_unlock(s_audio_global.recorder.mutex);
                break;             
            }


            if (s_audio_global.recorder.cachelen + RECORD_PCM_FRAME_BYTES > RECORD_PCM_CACHED_LEN) {
                WLLOGE("!!!!!!!!!!!!!!!!!!!!!!! overflow !!!!!!!!!!!!!!!!!!!!!!!!!!!");
                s_audio_global.recorder.cachelen = 0;
            }

            framelen = wl_ext_record_get_data(handle, s_audio_global.recorder.cache + s_audio_global.recorder.cachelen, RECORD_PCM_FRAME_BYTES);
            if (framelen < 0) {
                WLLOGW("audio recorder ql_pcm_read error");
                if (s_audio_global.recorder.end_callback) {
                    s_audio_global.recorder.end_callback(s_audio_global.recorder.handle, -1);
                }
                wl_ext_mutex_unlock(s_audio_global.recorder.mutex);
                return;
            } else {
                s_audio_global.recorder.cachelen += framelen;
                if (s_audio_global.recorder.cachelen >= RECORD_PCM_FRAME_BYTES) {
                    if (s_audio_global.recorder.callback) {
                        s_audio_global.recorder.callback(s_audio_global.recorder.handle, s_audio_global.recorder.cache, RECORD_PCM_FRAME_BYTES);
                    }
                    s_audio_global.recorder.cachelen -= RECORD_PCM_FRAME_BYTES;
                    if (s_audio_global.recorder.cachelen > 0) {
                        wl_char pcmframe[RECORD_PCM_FRAME_BYTES];
                        memcpy(pcmframe,
                            s_audio_global.recorder.cache + RECORD_PCM_FRAME_BYTES,
                            s_audio_global.recorder.cachelen);
                        memcpy(s_audio_global.recorder.cache,
                            pcmframe,
                            s_audio_global.recorder.cachelen);
                    }
                }
            }

            wl_ext_mutex_unlock(s_audio_global.recorder.mutex);
        }
        break;

        case WL_EXT_RECORD_ERROR: {
            WLLOGW("audio recorder error");
            if (s_audio_global.recorder.end_callback) {
                s_audio_global.recorder.end_callback(s_audio_global.recorder.handle, -1);
            }
            wl_ext_mutex_unlock(s_audio_global.recorder.mutex);    
        }
        break;

        case WL_EXT_RECORD_STOPPED: {
            WLLOGD("audio recorder stopped");
        }
        break;
    }
}

WL_AUDIO_HANDLE wl_audio_start_record(wl_audio_recorder_callback callback, wl_audio_end_callback end_callback) {
    // 先释放录音器
    wl_audio_stop_record(s_audio_global.recorder.handle);

    wl_ext_mutex_lock(s_audio_global.recorder.mutex, WL_EXT_WAIT_FOREVER);
    s_audio_global.recorder.handle = wl_ext_record_start(audio_recorder_callback, NULL);
    
    if (wl_null == s_audio_global.recorder.handle) {
        WLLOGE("wl_ext_record_startaudio_recorder_callback fail");
        wl_ext_mutex_unlock(s_audio_global.recorder.mutex);
        return wl_null;
    }

    s_audio_global.recorder.cachelen     = 0;
    s_audio_global.recorder.callback     = callback;
    s_audio_global.recorder.end_callback = end_callback;
    wl_ext_mutex_unlock(s_audio_global.recorder.mutex);
    // ql_set_audio_path_earphone();
    return s_audio_global.recorder.handle;
}

wl_int32 wl_audio_stop_record(WL_AUDIO_HANDLE handle) {
    wl_ext_mutex_lock(s_audio_global.recorder.mutex, WL_EXT_WAIT_FOREVER);
    if (handle == s_audio_global.recorder.handle) {
        WLLOGE("stop record handle %x", s_audio_global.recorder.handle);
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
    }
    wl_ext_mutex_unlock(s_audio_global.recorder.mutex);
    if (INVALID_AUDIO_HANDLE != handle) {
        wl_ext_record_stop(handle);
        WLLOGE("ql_pcm_record_deinit handle %x", handle);
    }
    WLLOGI("stop record success");
    return 0;
}


static void audio_player_callback(wl_ext_player_handle handle, int event, void* pContext) {
    switch (event) {
        case WL_EXT_PLAYER_STARTED: {
            WLLOGW("audio player started");
        }
        break;

        case WL_EXT_PLAYER_DATA_DRAIN: {
            int pcm_write = 0;
            wl_ext_mutex_lock(s_audio_global.player.mutex, WL_EXT_WAIT_FOREVER);
            if (INVALID_AUDIO_HANDLE == s_audio_global.player.handle) {
                WLLOGE("audio player handle is null");
                if (s_audio_global.player.end_callback) {
                    s_audio_global.player.end_callback(s_audio_global.player.handle, 0);
                }
                wl_ext_mutex_unlock(s_audio_global.player.mutex);
                break;
            }
            
            // 每次只写一帧,与定时器匹配
            if (PLAY_PCM_FRAME_BYTES > s_audio_global.player.cachelen) {
                memset(s_audio_global.player.cache, 0, PLAY_PCM_FRAME_BYTES);
            }
            pcm_write = wl_ext_player_feed_data(handle, s_audio_global.player.cache, PLAY_PCM_FRAME_BYTES);
            s_audio_global.player.cachelen = 0;
            // 通知GET数据
            if (s_audio_global.player.callback) {
                s_audio_global.player.callback(s_audio_global.player.handle);
            }
            wl_ext_mutex_unlock(s_audio_global.player.mutex);
        }
        break;
        
        case WL_EXT_PLAYER_END:
        case WL_EXT_PLAYER_ERROR: {
            WLLOGW("audio player end");
            wl_ext_mutex_lock(s_audio_global.player.mutex, WL_EXT_WAIT_FOREVER);
            if (s_audio_global.player.end_callback) {
                s_audio_global.player.end_callback(s_audio_global.player.handle, 0);
            }
            wl_ext_mutex_unlock(s_audio_global.player.mutex);
        }
    }
}

WL_AUDIO_HANDLE wl_audio_start_play(wl_audio_player_callback callback, wl_audio_end_callback end_callback) {
    wl_audio_stop_play(s_audio_global.player.handle);

    wl_ext_mutex_lock(s_audio_global.player.mutex, WL_EXT_WAIT_FOREVER);
    s_audio_global.player.handle = wl_ext_player_start(NULL, audio_player_callback);
    if (NULL == s_audio_global.player.handle) {
        WLLOGE("ql_pcm_open fail");
        wl_ext_mutex_unlock(s_audio_global.player.mutex);
        return INVALID_AUDIO_HANDLE;
    }

    s_audio_global.player.cachelen     = 0;
    s_audio_global.player.callback     = callback;
    s_audio_global.player.end_callback = end_callback;
    wl_ext_mutex_unlock(s_audio_global.player.mutex);
    return s_audio_global.player.handle;
}

wl_int32 wl_audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size) {
    wl_ext_mutex_lock(s_audio_global.player.mutex, WL_EXT_WAIT_FOREVER);
    if (INVALID_AUDIO_HANDLE != s_audio_global.player.handle) {
        if (data_size == PLAY_PCM_FRAME_BYTES) {
            memcpy(s_audio_global.player.cache, data, data_size);
            s_audio_global.player.cachelen = data_size;
        }
    }
    wl_ext_mutex_unlock(s_audio_global.player.mutex);

    return 0;
}

wl_int32 wl_audio_stop_play(WL_AUDIO_HANDLE handle) {
    wl_ext_mutex_lock(s_audio_global.player.mutex, WL_EXT_WAIT_FOREVER);
    if (handle == s_audio_global.player.handle) {
        WLLOGE("stop play wl_mutex_acquire handle %p", handle);
        s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
    }
    wl_ext_mutex_unlock(s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE != handle) {
        WLLOGE("wl_ext_player_stop handle %p", handle);
        wl_ext_player_stop(handle);
    }
    WLLOGI("stop play success");
    return 0;
}


wl_int32 audio_get_play_data(wl_uint8* data, wl_uint32 datalen) {
    wl_ext_mutex_lock(s_audio_global.player.mutex, WL_EXT_WAIT_FOREVER);
    if (s_audio_global.player.cachelen >= datalen) {
        memcpy(data, s_audio_global.player.cache, datalen);
        s_audio_global.player.cachelen -= datalen;
        if (s_audio_global.player.cachelen > 0) {
            memmove(s_audio_global.player.cache, s_audio_global.player.cache + datalen, s_audio_global.player.cachelen);
        }
        // 缓存区未满,拉取数据
        if (s_audio_global.player.cachelen <= (PLAY_PCM_CACHED_LEN - PLAY_PCM_FRAME_BYTES)) {
            // 通知GET数据
            if (s_audio_global.player.callback) {
                s_audio_global.player.callback(s_audio_global.player.handle);
            }
        }
        wl_ext_mutex_unlock(s_audio_global.player.mutex);
        return 0;
    }
    memset(data, 0, datalen);
    // 通知GET数据
    if (s_audio_global.player.callback) {
        s_audio_global.player.callback(s_audio_global.player.handle);
    }
    wl_ext_mutex_unlock(s_audio_global.player.mutex);
    return -1;
}

wl_int32 audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size) {
    wl_ext_mutex_lock(s_audio_global.player.mutex, WL_EXT_WAIT_FOREVER);
    if (INVALID_AUDIO_HANDLE != s_audio_global.player.handle) {
        if (data_size <= PLAY_PCM_CACHED_LEN - s_audio_global.player.cachelen) {
            memcpy(s_audio_global.player.cache + s_audio_global.player.cachelen, data, data_size);
            s_audio_global.player.cachelen += data_size;
        }
    }
    wl_ext_mutex_unlock(s_audio_global.player.mutex);
    return 0;
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
